#pragma once

#include "StateMachine.h"

#include "Pseudostate.h"
#include "RedefinableElement.h"
#include "Region.h"
#include "State.h"
#include "Vertex.h"

namespace uml {

StateMachine::StateMachine(const QString& umlType)
    : Behavior(umlType) {}

void StateMachine::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    Behavior::fromJson(json, errorList);
    JsonSerializable::fromJsonArray(connectionPoints_, "connectionPoints", json, errorList);
    JsonSerializable::fromJsonArray(extendedStateMachines_, "extendedStateMachines", json, errorList);
    JsonSerializable::fromJsonArray(regions_, "regions", json, errorList);
    JsonSerializable::fromJsonArray(submachineStates_, "submachineStates", json, errorList);
}

void StateMachine::toJson(QJsonObject& json) {
    Behavior::toJson(json);
    JsonSerializable::toJsonArray(connectionPoints_, "connectionPoints", json);
    JsonSerializable::toJsonArray(extendedStateMachines_, "extendedStateMachines", json);
    JsonSerializable::toJsonArray(regions_, "regions", json);
    JsonSerializable::toJsonArray(submachineStates_, "submachineStates", json);
}

/// <summary>
/// LCA
    /// The operation LCA(s1,s2) returns the Region that is the least common ancestor of
    /// Vertices s1 and s2, based on the StateMachine containment hierarchy.
/// </summary>
    /// <code>
    /// result = (if ancestor(s1, s2) then 
    ///     s2.container
    /// else
    /// 	if ancestor(s2, s1) then
    /// 	    s1.container 
    /// 	else 
    /// 	    LCA(s1.container.state, s2.container.state)
    /// 	endif
    /// endif)
    /// </code>
shared_ptr<Region> StateMachine::LCA(shared_ptr<Vertex> s1, shared_ptr<Vertex> s2) const
{
    return nullptr;
}

/// <summary>
/// ancestor
    /// The query ancestor(s1, s2) checks whether Vertex s2 is an ancestor of Vertex s1.
/// </summary>
    /// <code>
    /// result = (if (s2 = s1) then 
    /// 	true 
    /// else 
    /// 	if s1.container.stateMachine->notEmpty() then 
    /// 	    true
    /// 	else 
    /// 	    if s2.container.stateMachine->notEmpty() then 
    /// 	        false
    /// 	    else
    /// 	        ancestor(s1, s2.container.state)
    /// 	     endif
    /// 	 endif
    /// endif  )
    /// </code>
bool StateMachine::ancestor(shared_ptr<Vertex> s1, shared_ptr<Vertex> s2) const
{
    return false;
}

/// <summary>
/// isConsistentWith
    /// The query isConsistentWith() specifies that a redefining StateMachine is
    /// consistent with a redefined StateMachine provided that the redefining
    /// StateMachine is an extension of the redefined StateMachine : Regions are
    /// inherited and Regions can be added, inherited Regions can be redefined. In case
    /// of multiple redefining StateMachine, extension implies that the redefining
    /// StateMachine gets orthogonal Regions for each of the redefined StateMachine.
/// </summary>
    /// <code>
    /// result = (-- the following is merely a default body; it is expected that the specific form of this constraint will be specified by profiles
    /// true)
    /// </code>
bool StateMachine::isConsistentWith(shared_ptr<RedefinableElement> redefiningElement) const
{
    return false;
}

/// <summary>
/// isRedefinitionContextValid
    /// The query isRedefinitionContextValid() specifies whether the redefinition
    /// context of a StateMachine is properly related to the redefinition contexts of
    /// the specified StateMachine to allow this element to redefine the other. The
    /// context Classifier of a redefining StateMachine must redefine the context
    /// Classifier of the redefined StateMachine.
/// </summary>
    /// <code>
    /// result = (if redefinedElement.oclIsKindOf(StateMachine) then
    ///   let redefinedStateMachine : StateMachine = redefinedElement.oclAsType(StateMachine) in
    ///     self._'context'().oclAsType(BehavioredClassifier).redefinedClassifier->
    ///       includes(redefinedStateMachine._'context'())
    /// else
    ///   false
    /// endif)
    /// </code>
bool StateMachine::isRedefinitionContextValid(shared_ptr<RedefinableElement> redefinedElement) const
{
    return false;
}

/// <summary>
/// LCAState
    /// This utility funciton is like the LCA, except that it returns the nearest
    /// composite State that contains both input Vertices.
/// </summary>
    /// <code>
    /// result = (if v2.oclIsTypeOf(State) and ancestor(v1, v2) then
    /// 	v2.oclAsType(State)
    /// else if v1.oclIsTypeOf(State) and ancestor(v2, v1) then
    /// 	v1.oclAsType(State)
    /// else if (v1.container.state->isEmpty() or v2.container.state->isEmpty()) then 
    /// 	null.oclAsType(State)
    /// else LCAState(v1.container.state, v2.container.state)
    /// endif endif endif)
    /// </code>
shared_ptr<State> StateMachine::LCAState(shared_ptr<Vertex> v1, shared_ptr<Vertex> v2) const
{
    return nullptr;
}

/// <summary>
/// connectionPoints getter
/// The connection points defined for this StateMachine. They represent the
/// interface of the StateMachine when used as part of submachine State
/// </summary>
vector<shared_ptr<Pseudostate>>& StateMachine::connectionPoints() {
    return connectionPoints_;
}

/// <summary>
/// extendedStateMachines getter
/// The StateMachines of which this is an extension.
/// </summary>
vector<shared_ptr<StateMachine>>& StateMachine::extendedStateMachines() {
    return extendedStateMachines_;
}

/// <summary>
/// regions getter
/// The Regions owned directly by the StateMachine.
/// </summary>
vector<shared_ptr<Region>>& StateMachine::regions() {
    return regions_;
}

/// <summary>
/// submachineStates getter
/// References the submachine(s) in case of a submachine State. Multiple machines
/// are referenced in case of a concurrent State.
/// </summary>
vector<shared_ptr<State>>& StateMachine::submachineStates() {
    return submachineStates_;
}

} // namespace uml
